using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Text.RegularExpressions;

namespace Athena.Core
{
    /// <summary>
    /// An Edge represents a reaction. The reaction can either be a proper reaction, where reactants
    /// are converted to products, or it can be a regulation, where some modifiers are influencing the 
    /// activity of a Part or some other Node. IsReaction and IsRegulation are used to make this 
    /// distinction. 
    /// The regulatory Edges are mostly PoPSRate reactions, which is a class inside Part. 
    /// </summary>
    [Serializable()]
    public class Edge : Item
    {
        public static int COUNT = 0;
        public static bool AUTO_MODIFIERS = true;
        /// <summary>
        /// This is a flag to indicate that the rate expression is not a function
        /// of the reactants, which can be problematic (generally)
        /// </summary>
        public bool PossibleErrorInRate = false;

        /// <summary>
        /// Returns a duplicate
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Edge(this);
        }        

        /// <summary>
        /// Makes a BezierConnector representing this edge. If the Nodes participating in this 
        /// edge do not have drawables, this method calls GenerateDrawables for each of those
        /// Nodes. 
        /// </summary>
        /// <param name="point">the point at which to make the drawable</param>
        public override void GenerateDrawable(System.Drawing.PointF point)
        {
            if (_Reactants == null || _Products == null) return;

            List<Objects.IDrawable> inputs = new List<Athena.Objects.IDrawable>();
            List<Objects.IDrawable> outputs = new List<Athena.Objects.IDrawable>();
            
            foreach (Node n in _Reactants.Keys)
            {
                if (n.Drawable == null) n.GenerateDrawable(point);
                inputs.Add(n.Drawable);
            }

            List<Edge> pops = new List<Edge>();
            foreach (Node n in _Products.Keys)
            {
                if (n.Drawable == null) n.GenerateDrawable(point);
                if (n is Part) pops.Add(((Part)n).PoPS.Clone() as Edge);
                outputs.Add(n.Drawable);                
            }
            
            _Drawable = new Objects.BezierConnector(inputs, outputs, false);

            int i = 0;
            foreach (Node n in _Products.Keys)
            {
                if (n is Part && i < pops.Count) ((Part)n).PoPS = pops[i];
                ++i;
            }

            _Drawable.Item = this;
            
            if (IsRegulation)
            {
                if (_ReactionType == "+")
                    ((Objects.BezierConnector)_Drawable).ArrowHead = Athena.Objects.Connector.ArrowHeadType.Circle;
                else
                    ((Objects.BezierConnector)_Drawable).ArrowHead = Athena.Objects.Connector.ArrowHeadType.Bar;

                ((Objects.BezierConnector)_Drawable).GenerateModifiers();
            }
        }

        /// <summary>
        /// Make an edge from another edge. Warning: the input/output nodes are not duplicated
        /// </summary>
        /// <param name="copy"></param>
        public Edge(Edge copy)
        {
            _Rate = copy._Rate;
            _ID = copy._ID;
            _DBID = copy._DBID;
            _Type = copy._Type;
            _IsRegulation = copy._IsRegulation;
            _Drawable = copy._Drawable;
            _Parent = copy._Parent;
            
            /*foreach (Node n in copy._Reactants.Keys)
            {
                double d = (double)copy._Reactants[n];
                AddReactant(n,d);
            }
            foreach (Node n in copy._Products.Keys)
            {
                double d = (double)copy._Products[n];
                AddProduct(n, d);
            }
            foreach (Node n in copy._Modifiers.Keys)
            {
                double d = (double)copy._Modifiers[n];
                AddModifier(n, d);
            }*/

            _Reactants = copy._Reactants.Clone() as Hashtable;
            _Products = copy._Products.Clone() as Hashtable;
            _Modifiers = copy._Modifiers.Clone() as Hashtable;
        }

        /// <summary>
        /// Hashtables contain Node->double pairs
        /// </summary>
        protected Hashtable _Reactants = new Hashtable(),
                            _Products = new Hashtable(),
                            _Modifiers = new Hashtable();

        /// <summary>
        /// Returns the hashtable of node->parameter
        /// This is the actual hashtable that the edge uses to store its nodes
        /// </summary>
        public virtual Hashtable ReactantsHash
        {
            get { return _Reactants; }
            set { _Reactants = new Hashtable(value); }
        }
        /// <summary>
        /// Returns the hashtable of node->parameter
        /// This is the actual hashtable that the edge uses to store its nodes
        /// </summary>
        public virtual Hashtable ProductsHash
        {
            get { return _Products; }
            set { _Products = new Hashtable(value); }
        }
        /// <summary>
        /// Returns the hashtable of node->parameter
        /// This is the actual hashtable that the edge uses to store its nodes
        /// </summary>
        public virtual Hashtable ModifiersHash
        {
            get { return _Modifiers; }
            set { _Modifiers = new Hashtable(value); }
        }

        /// <summary>
        /// The rate expression for this edge
        /// </summary>
        protected string _Rate;

        /// <summary>
        /// Change the name of a parameter in the rate equation. Changing
        /// the rate equation can affect modifiers. 
        /// </summary>
        /// <param name="oldname"></param>
        /// <param name="newname"></param>
        public virtual void ChangeParameterName(string oldname, string newname)
        {
            ChangeName(oldname, newname);
        }

        /// <summary>
        /// Change the name of a word in the rate equation without changing the rate
        /// equation itself. 
        /// Warning: Changing rate can always affect the list of modifiers.
        /// </summary>
        /// <param name="oldname"></param>
        /// <param name="newname"></param>
        public virtual void ChangeName(string oldname, string newname)
        {
            if (oldname == newname) return;

            string rate = ' ' + _Rate + ' ';

            string[] ops = Functions.TScanner.Operators;
            foreach (string op in ops)
            {
                rate = rate.Replace(op, ' ' + op + ' ');
            }
            rate = rate.Replace(" " + oldname + " ", " " + newname + " ");
            
            Rate = rate.Replace(" ", "");
        }

        public string _ReactionType = "-";
        protected bool _IsRegulation = false;

        /// <summary>
        /// Checks whether this edge represents a modification rather than mass flow
        /// </summary>
        /// <returns></returns>
        public virtual bool IsRegulation
        {
            get
            {
                if (_Products != null)
                    foreach (Node n in _Products.Keys)
                    {
                        if (n is Part)
                        {
                            _IsRegulation = true;
                            return true;
                        }
                    }
                return _IsRegulation;
            }
            set
            {
                _IsRegulation = value;
            }
        }

        /// <summary>
        /// Gets a parameter value for this reaction
        /// </summary>
        /// <param name="k"></param>
        /// <param name="d"></param>
        public virtual double GetParameter(string name)
        {
            Module parent = ParentModule;
            if (parent != null)
            {
                return parent.GetParameter(name);
            }

            return double.NaN;
        }

        /// <summary>
        /// Sets a parameter value for this reaction
        /// </summary>
        /// <param name="k"></param>
        /// <param name="d"></param>
        public virtual void SetParameter(string name, double d)
        {
            Module parent = ParentModule;
            if (parent != null)
            {
                parent.SetParameter(name, d);
            }
        }
        /*
        /// <summary>
        /// Set the parameters using a single semi-colon delimited string
        /// </summary>
        /// <param name="parameters">semi-colon delimited string</param>
        public virtual void SetParameterInitializations(string ParameterInits, char sep)
        {
            if (string.IsNullOrEmpty(ParameterInits) || !ParameterInits.Contains("=")) return;

            string[] sAssignments = ParameterInits.Split(new char[] { sep }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string s in sAssignments)
            {
                string[] pair = s.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                try
                {
                    pair[0] = pair[0].Trim();
                    pair[1] = pair[1].Trim();
                    SetParameter(pair[0], double.Parse(pair[1]));
                }
                catch (Exception) { }
            }

        }
        
        /// <summary>
        /// Gets the parameter initializations (semi-colon separated eg.: k1=2;k3=0.5;)
        /// </summary>
        public virtual string GetParameterInitializations(string sep)
        {
            string sParameterInitializations = "";

            List<string> parameterNames = ParameterNames;

            foreach (string s in parameterNames)
            {
                double d = GetParameter(s);
                if (double.IsNaN(d))
                {
                    SetParameter(s, 0.1);
                    d = 0.1;
                }
                sParameterInitializations += string.Format("{0} = {1}{2}", s, d, sep);
            }

            return sParameterInitializations;
        }
        */

        protected List<string> _ParameterNames = new List<string>();
        /// <summary>
        /// Get the parameters involved in this reaction
        /// </summary>
        public List<string> ParameterNames
        {
            get
            {
                /*if (_Inputs == null) _Inputs = new Hashtable();
                if (_Outputs == null) _Outputs = new Hashtable();
                if (_Modifiers == null) _Modifiers = new Hashtable();

                List<string> ParamNames = new List<string>();

                if (string.IsNullOrEmpty(_Rate)) return ParamNames;

                string[] words;

                if (!string.IsNullOrEmpty(_Rate))
                {
                    string[] words1 = _Rate.Split(' ');
                    words = new string[words1.Length];
                    for (int i = 0; i < words1.Length; ++i)
                        words[i] = words1[i];
                }
                else
                {
                    if (!string.IsNullOrEmpty(_Rate))
                        words = _Rate.Split(' ');
                    else
                        words = null;
                }

                if (words == null || words.Length < 1) return ParamNames;

                for (int i = 0; i < words.Length; ++i)
                {
                    string word = words[i];

                    bool isParam = true;

                    //if an operator, then no
                    if (word.Length == 0 ||
                        word.ToLower().Equals("pi") || word.ToLower().Equals("exponentiale") ||
                        word.Equals("(") || word.Equals(")") || word.Equals(",") ||
                        word.Equals("-") || word.Equals("+") || word.Equals("*") ||
                        word.Equals("/") || word.Equals("^"))
                        isParam = false;

                    //function
                    if (i < words.Length - 1 && words[i + 1].Equals("("))
                        isParam = false;

                    //number
                    if (isParam)
                    {
                        if (word.Length > 0 && word[0] >= (int)'0' && word[0] <= (int)'9')
                        {
                            int j = 0;
                            while (j < word.Length && word[j] >= (int)'0' && word[j] <= (int)'9')
                            {
                                ++j;
                            }
                            word = word.Remove(0, j);
                        }

                        if (word.Length < 1)
                            isParam = false;
                    }

                    //see if the word is any of the Node IDs
                    if (isParam)
                        foreach (Node n in _Inputs.Keys)
                            if (word.Equals(n.ID))
                            {
                                isParam = false;
                                break;
                            }
                    if (isParam)
                        foreach (Node n in _Outputs.Keys)
                            if (word.Equals(n.ID))
                            {
                                isParam = false;
                                break;
                            }
                    if (isParam)
                    {
                        foreach (Node n in _Modifiers.Keys)
                            if (word.Equals(n.ID))
                            {
                                isParam = false;
                                break;
                            }

                        if (Parent is Container)
                        {
                            Item item = ((Container)Parent).FindItem(word);
                            if (item != null && item is Node && !_Modifiers.Contains((Node)item))
                            {
                                _Modifiers[((Node)item)] = 1.0d;
                                isParam = false;
                            }
                        }
                    }

                    //if the word is not an ID or an operator, then it must be a parameter
                    if (isParam && !ParamNames.Contains(word))
                    {
                        ParamNames.Add(word);
                    }
                }
                return ParamNames;
                 */

                return _ParameterNames;
            }
        }

        /// <summary>
        /// returns a hashtable of string->parameter
        /// changing these parameter values will change its value
        /// in all places where the parameter is used
        /// </summary>
        public virtual Hashtable ParametersHash
        {
            get
            {
                if (ParentModule == null) return null;

                List<string> parameters = ParameterNames;
                Hashtable paramsHash = new Hashtable();
                Hashtable parentParams = ParentModule.LocalParameters;
                foreach (string s in parentParams.Keys)
                {
                    if (parameters.Contains(s))
                    {
                        paramsHash[s] = parentParams[s];
                    }
                }
                return paramsHash;
            }
        }

        /// <summary>
        /// Updates the list of parameters from the rate equation
        /// </summary>
        public virtual void UpdateParameters()
        {
            Module parent = ParentModule;
            if (parent != null)
            {
                parent.UpdateParameters();
            }
        }

        public virtual Node FindNode(string nodeID)
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            List<Module> modules = new List<Module>();

            foreach (Node n in _Reactants.Keys)   //check reactants first
            {
                if (n.ID == nodeID)
                    return n;
                Module module = n.ParentModule;
                if (module != null)
                {
                    if (!modules.Contains(module)) modules.Add(module);
                }
            }

            foreach (Node n in _Modifiers.Keys)
            {
                if (n.ID == nodeID)
                    return n;
                Module module = n.ParentModule;
                if (module != null)
                {
                    if (!modules.Contains(module)) modules.Add(module);
                }
            }

            foreach (Node n in _Products.Keys)
            {
                if (n.ID == nodeID)
                    return n;
                Module module = n.ParentModule;
                if (module != null)
                {
                    if (!modules.Contains(module)) modules.Add(module);
                }
            }

            foreach (Module module in modules)
            {
                Node n = module.FindItem(nodeID) as Node; //check all modules
                if (n != null)
                    return n;
            }

            if (ParentModule != null)
                return ParentModule.FindItem(nodeID) as Node;

            return null;
        }

        /// <summary>
        /// Get the forward rate law in the context of the given module
        /// Use the GetRate with 2 args if you want to get rates for several edges in the 
        /// same module
        /// </summary>
        /// <returns></returns>
        public virtual string GetRate(Module module)
        {
            if (module == null) return _Rate;

            Hashtable allItems = module.AllItems;

            string rate = ' ' + _Rate + ' ';

            string[] ops = Functions.TScanner.Operators;
            foreach (string op in ops)
            {
                rate = rate.Replace(op, ' ' + op + ' ');
            }
            string[] words = rate.Split(' ');

            Item parent = Parent;

            foreach (string s in words)
            {
                string word = s.Trim();
                if (!(word.Length == 0 ||
                        word.ToLower().Equals("pi") || word.ToLower().Equals("exponentiale") ||
                        word.Equals("(") || word.Equals(")") || word.Equals(",") ||
                        word.Equals("-") || word.Equals("+") || word.Equals("*") ||
                        word.Equals("/") || word.Equals("^")))
                {
                    Node node = FindNode(word);
                    string newword = word;

                    if (node != null)
                        newword = module.Rename(word, node.Parent, allItems);
                    else
                        newword = module.Rename(word, Parent, allItems);

                    rate = rate.Replace(' ' + word + ' ', newword);                    
                }
            }
            rate = rate.Replace(" ", "");
            return rate;
        }

        /// <summary>
        /// Get the forward rate law in the context of the given module
        /// Use this method to avoid recalculating Module.AllItems
        /// </summary>
        /// <returns></returns>
        public virtual string GetRate(Module module, Hashtable allItems)
        {
            if (module == null) return _Rate;

            string rate = ' ' + _Rate + ' ';

            string[] ops = Functions.TScanner.Operators;
            foreach (string op in ops)
            {
                rate = rate.Replace(op, ' ' + op + ' ');
            }
            string[] words = rate.Split(' ');

            Item parent = Parent;

            foreach (string s in words)
            {
                string word = s.Trim();
                if (!(word.Length == 0 ||
                        word.ToLower().Equals("pi") || word.ToLower().Equals("exponentiale") ||
                        word.Equals("(") || word.Equals(")") || word.Equals(",") ||
                        word.Equals("-") || word.Equals("+") || word.Equals("*") ||
                        word.Equals("/") || word.Equals("^")))
                {
                    Node node = FindNode(word);
                    string newword = word;

                    if (node != null)
                        newword = module.Rename(word, node.Parent, allItems);
                    else
                        newword = module.Rename(word, Parent, allItems);

                    rate = rate.Replace(' ' + word + ' ', newword);
                }
            }
            rate = rate.Replace(" ", "");
            return rate;
        }

        /// <summary>
        /// Updates the modifiers and parameters list
        /// </summary>
        public virtual void Update()
        {
            Rate = _Rate;
        }

        /// <summary>
        /// Get and Set the forward rate law
        /// To get the rate inside a specific module, use GetRate
        /// </summary>
        /// <returns></returns>
        public virtual string Rate
        {
            get
            {
                return _Rate;
            }
            set
            {
                if (string.IsNullOrEmpty(value)) return;

                if (_Reactants == null) _Reactants = new Hashtable();
                if (_Products == null) _Products = new Hashtable();
                if (_Modifiers == null) _Modifiers = new Hashtable();

                string rate = value;

                try
                {
                    PossibleErrorInRate = true;
                    Functions.TAlgExpression ae = new Functions.TAlgExpression();
                    ae.AssignBehaviour = Functions.TAssignBehaviour.eInstallSymbols;
                    ae.compile(rate);
                    Functions.TTreeNode[] nodes = ae.GetAllSymbols();
                    List<Node> modifiers = new List<Node>();
                    _ParameterNames.Clear();
                    foreach (Functions.TTreeNode tn in nodes)
                    {
                        bool isParam = true;
                        foreach (Node n in _Reactants.Keys)
                        {
                            if (tn.name == n.ID)
                            {
                                isParam = false;
                                PossibleErrorInRate = false;  //reactant in rate expression
                            }
                        }
                        if (isParam)
                            foreach (Node n in _Products.Keys)
                            {
                                if (tn.name == n.ID) isParam = false;
                            }
                        if (isParam)
                        {
                            Module parent = ParentModule;
                            if (parent != null)
                            {
                                Item node = parent.FindItem(tn.name);
                                if (node is Node)
                                {
                                    modifiers.Add(node as Node);
                                    isParam = false;
                                }
                                else
                                    if (node is Edge)  //flux names allowed in the rate law
                                    {
                                        PossibleErrorInRate = true;
                                        return;
                                    }
                            }
                        }
                        if (isParam)
                        {
                            if (!_ParameterNames.Contains(tn.name))
                                _ParameterNames.Add(tn.name);
                        }
                    }
                    foreach (Node n in modifiers)
                    {
                        if (!_Modifiers.Contains(n))
                        {
                            if (AUTO_MODIFIERS) AddModifier(n);
                        }
                    }
                    List<Node> currentmodifiers = new List<Node>();
                    foreach (Node n in _Modifiers.Keys) 
                        currentmodifiers.Add(n);

                    foreach (Node n in currentmodifiers)
                    {
                        if (!modifiers.Contains(n))
                        {
                            _Modifiers.Remove(n);
                            n.RemoveOutgoingEdge(this);
                        }
                    }
                }
                catch (Exception)
                {
                    return;
                }

                _Rate = rate;

                Module mparent = ParentModule;
                if (mparent != null)
                {
                    mparent.UpdateParameters();
                }
                if (AUTO_MODIFIERS)
                    foreach (Node n in _Products.Keys)
                    {
                        if (n is Part && ((Part)n).PoPS.Rate != _Rate)
                        {
                            ((Part)n).PoPS.Rate = _Rate;
                        }
                    }
            }
        }
        /// <summary>
        /// Get/Set the parent of theis reaction. 
        /// </summary>
        public override Item Parent
        {
            get
            {
                return base.Parent;
            }
            set
            {
                base.Parent = value;
                if (ParentModule != null)
                {
                    Rate = _Rate;  //re-evaluate parameters and modifiers
                }
            }
        }
        /// <summary>
        /// Checks whether the forward rate is zero
        /// </summary>
        /// <returns></returns>
        public virtual bool RateIsZero
        {
            get { return string.IsNullOrEmpty(_Rate) || _Rate.Trim().Equals("0"); }
        }

        /// <summary>
        /// get and set the inputs for this reaction
        /// </summary>
        public virtual Node[] Reactants
        {
            get
            {
                Node[] nodes = new Node[_Reactants.Count];
                int i = 0;
                foreach (Node n in _Reactants.Keys)
                {
                    nodes[i] = n;
                    ++i;
                }
                return nodes;
            }
            set
            {
                _Reactants.Clear();
                foreach (Node n in value)
                {
                    AddReactant(n);
                }
            }
        }

        /// <summary>
        /// get and set the output for this reaction
        /// </summary>
        public virtual Node[] Products
        {
            get
            {
                Node[] nodes = new Node[_Products.Count];
                int i = 0;
                foreach (Node n in _Products.Keys)
                {
                    nodes[i] = n;
                    ++i;
                }
                return nodes;
            }
            set
            {
                _Products.Clear();
                foreach (Node n in value)
                {
                    AddProduct(n);
                }
            }
        }

        /// <summary>
        /// Get and set the input stoichiometry for this reaction
        /// </summary>
        public virtual double[] ReactantsStoichiometry
        {
            get
            {
                double[] values = new double[_Reactants.Count];
                int i = 0;
                foreach (Node n in _Reactants.Keys)
                {
                    values[i] = (double)_Reactants[n];
                    ++i;
                }
                return values;
            }
        }

        /// <summary>
        /// gets all the input and output and modifier nodes in this reaction
        /// </summary>
        public virtual List<Node> Nodes
        {
            get
            {
                List<Node> list = new List<Node>();
                list.AddRange(Reactants);
                list.AddRange(Products);
                list.AddRange(Modifiers);
                return list;
            }
        }

        /// <summary>
        /// get and set the outputs for this reaction
        /// </summary>
        public virtual double[] ProductsStoichiometry
        {
            get
            {
                double[] values = new double[_Products.Count];
                int i = 0;
                foreach (Node n in _Products.Keys)
                {
                    values[i] = (double)_Products[n];
                    ++i;
                }
                return values;
            }
        }

        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetStoichiometry(Node node, double value)
        {
            if (_Reactants.Contains(node)) _Reactants[node] = value;
            if (_Products.Contains(node)) _Products[node] = value;
            if (_Modifiers.Contains(node)) _Modifiers[node] = value;
        }

        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetReactantStoichiometry(Node node, double value)
        {
            if (_Reactants.Contains(node)) _Reactants[node] = value;
        }

        /// <summary>
        /// set the stoichiometry for the given set of nodes in the same order
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="values"></param>
        public virtual void SetReactantsStoichiometry(Node[] nodes, double[] values)
        {
            if (_Reactants == null || nodes == null || values == null ||nodes.Length != values.Length) return;
            int i = 0;
            foreach (Node n in nodes)
            {
                _Reactants[n] = values[i];
            }
        }

        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node id to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetReactantStoichiometry(string nodeID, double value)
        {
            Node node = FindNode(nodeID);
            if (node != null && _Reactants.Contains(node)) _Reactants[node] = value;
        }
        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetProductStoichiometry(Node node, double value)
        {
            if (node != null && _Products.Contains(node)) _Products[node] = value;
        }

        /// <summary>
        /// set the stoichiometry for the given set of nodes in the same order
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="values"></param>
        public virtual void SetProductsStoichiometry(Node[] nodes, double[] values)
        {
            if (_Products == null || nodes == null || values == null || nodes.Length != values.Length) return;
            int i = 0;
            foreach (Node n in nodes)
            {
                _Products[n] = values[i];
            }
        }
        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node id to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetProductStoichiometry(string nodeID, double value)
        {
            Node node = FindNode(nodeID);
            if (node != null && _Products.Contains(node)) _Products[node] = value;
        }
        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetModifierStoichiometry(Node node, double value)
        {
            if (node != null && _Modifiers.Contains(node)) _Modifiers[node] = value;
        }
        /// <summary>
        /// set the stoichiometry for the given set of nodes in the same order
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="values"></param>
        public virtual void SetModifiersStoichiometry(Node[] nodes, double[] values)
        {
            if (_Modifiers == null || nodes == null || values == null || nodes.Length != values.Length) return;
            int i = 0;
            foreach (Node n in nodes)
            {
                _Modifiers[n] = values[i];
            }
        }
        /// <summary>
        /// Change the stoichiometry of the given node. Nothing will be done if this node is
        /// not part of this reaction.
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual void SetModifierStoichiometry(string nodeID, double value)
        {
            Node node = FindNode(nodeID);
            if (node != null && _Modifiers.Contains(node)) _Modifiers[node] = value;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetStoichiometry(Node node)
        {
            double r = 0, p = 0;
            if (_Reactants.Contains(node)) r = (double)_Reactants[node];
            if (_Products.Contains(node)) p = (double)_Products[node];

            if (r != 0 && p != 0) return p - r;
            if (r != 0) return r;
            if (p != 0) return p;

            if (node != null && _Modifiers.Contains(node)) return (double)_Modifiers[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node ID to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetStoichiometry(string nodeID)
        {
            Node node = FindNode(nodeID);
            double r = 0, p = 0;
            if (_Reactants.Contains(node)) r = (double)_Reactants[node];
            if (_Products.Contains(node)) p = (double)_Products[node];

            if (r != 0 && p != 0) return p - r;
            if (r != 0) return r;
            if (p != 0) return p;

            if (node != null && _Modifiers.Contains(node)) return (double)_Modifiers[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetReactantStoichiometry(Node node)
        {
            if (node != null && _Reactants.Contains(node)) return (double)_Reactants[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node ID to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetReactantStoichiometry(string nodeID)
        {
            Node node = FindNode(nodeID);
            if (node != null && _Reactants.Contains(node)) return (double)_Reactants[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetProductStoichiometry(Node node)
        {
            if (node != null && _Products.Contains(node)) return (double)_Products[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node ID to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetProductStoichiometry(string nodeID)
        {
            Node node = FindNode(nodeID);
            if (node != null && _Products.Contains(node)) return (double)_Products[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetModifierStoichiometry(Node node)
        {
            if (node != null && _Modifiers.Contains(node)) return (double)_Modifiers[node];
            return 0;
        }
        /// <summary>
        /// Get the stoichiometry of the given node. A 0 will be returned if this node
        /// is not part of this reaction
        /// </summary>
        /// <param name="node">target node to change</param>
        /// <param name="value">the stoichiometry value</param>
        public virtual double GetModifierStoichiometry(string nodeID)
        {
            Node node = FindNode(nodeID);
            if (node != null && _Modifiers.Contains(node)) return (double)_Modifiers[node];
            return 0;
        }
        /// <summary>
        /// get and set the modifiers for this reaction
        /// </summary>
        public virtual Node[] Modifiers
        {
            get 
            {
                Node[] nodes = new Node[_Modifiers.Count];
                int i =0;
                foreach (Node n in _Modifiers.Keys)
                {
                    nodes[i] = n;
                    ++i;
                }
                return nodes; 
            }
            set 
            {
                _Modifiers.Clear();
                foreach (Node n in value)
                {
                    _Modifiers[n] = 1.0d;
                }
            }
        }

        /// <summary>
        /// copy all the modifiers from another edge
        /// </summary>
        /// <param name="copy"></param>
        public virtual void CopyModifiers(Edge copy)
        {
            foreach (Node n in _Modifiers.Keys)
                n.RemoveOutgoingEdge(this);
            _Modifiers = new Hashtable(copy._Modifiers);
            foreach (Node n in _Modifiers.Keys)
                n.AddOutgoingEdge(this);
        }

        /// <summary>
        /// get the stoichiometry for the modifiers in this reaction
        /// use SetModifierStoichiometry to set the stoichiometry -- this
        /// is maintain the node-stoichiometry order
        /// </summary>
        public virtual double[] ModifierStoichiometry
        {
            get
            {
                double[] values = new double[_Modifiers.Count];
                int i = 0;
                foreach (Node n in _Modifiers.Keys)
                {
                    values[i] = (double)_Modifiers[n];
                    ++i;
                }
                return values;
            }
        }

        /// <summary>
        /// The default mass action rate
        /// </summary>
        public string DefaultRate
        {
            get
            {
                string str = "k0";
                
                if (_Reactants != null && _Reactants.Count > 0)
                    foreach (Node n in _Reactants.Keys)
                    {
                        double d = (double)_Reactants[n];
                        if (d != 1)
                            str += "*" + n.ID + "^" + d;
                        else
                            str += "*" + n.ID;
                    }
                return str;
            }
        }

        /// <summary>
        /// Adds a new node to the inputs of this edge. Updates the rate law by multiplying the new node with the original rate
        /// </summary>
        /// <param name="input"></param>
        public virtual void AddReactant(Node input)
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Reactants.Contains(input))
                _Reactants[input] = 1.0d + (double)_Reactants[input];
            else
                _Reactants[input] = 1.0d;
            input.AddOutgoingEdge(this);
        }

        /// <summary>
        /// Adds a new node to the inputs of this edge. Updates the rate law by multiplying the new node with the original rate
        /// </summary>
        /// <param name="input"></param>
        public virtual void AddReactant(Node input, double stoichiometry)
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            _Reactants[input] = stoichiometry;
            input.AddOutgoingEdge(this);
        }

        /// <summary>
        /// Removes an input from the inputs. Does not change rate law 
        /// </summary>
        /// <param name="input"></param>
        public virtual void RemoveReactant(Node input)
        {
            if (input == null) return;
            if (_Reactants == null) _Reactants = new Hashtable();

            if (_Reactants.Contains(input))
            {
                _Reactants.Remove(input);
                input.RemoveOutgoingEdge(this);
            }
        }

        /// <summary>
        /// Adds a new output to the edge. Does not affect the rate law
        /// </summary>
        /// <param name="output"></param>
        public virtual void AddProduct(Node output)
        {
            if (_Products == null) _Products = new Hashtable();
            if (_Products.Contains(output))
                _Products[output] = 1.0d + (double)_Products[output];
            else
               _Products[output] = 1.0d;
            output.AddIncomingEdge(this);
        }
        /// <summary>
        /// Adds a new output to the edge. Does not affect the rate law
        /// </summary>
        /// <param name="output"></param>
        public virtual void AddProduct(Node output, double stoichiometry)
        {
            if (_Products == null) _Products = new Hashtable();
            _Products[output] = stoichiometry;
            output.AddIncomingEdge(this);
        }
        /// <summary>
        /// Removes an output from the edge. Does not affect the rate law. 
        /// </summary>
        /// <param name="output"></param>
        public virtual void RemoveProduct(Node output)
        {
            if (output == null) return;
            if (_Products == null) _Products = new Hashtable();

            if (_Products.Contains(output))
            {
                _Products.Remove(output);
                output.RemoveIncomingEdge(this);
            }
        }
        /// <summary>
        /// Add a modifier node
        /// </summary>
        /// <param name="node"></param>
        public virtual void AddModifier(params Node[] nodes)
        {
            if (_Modifiers == null) _Modifiers = new Hashtable();
            foreach (Node n in nodes)
                if (n != null)
                {
                    if (_Modifiers.Contains(n))
                        _Modifiers[n] = 1.0d + (double)_Modifiers[n];
                    else
                        _Modifiers[n] = 1.0d;
                    n.AddOutgoingEdge(this);
                }
        }
        /// <summary>
        /// Add a modifier node with stoichiometry
        /// </summary>
        /// <param name="node"></param>
        public virtual void AddModifier(Node node, double stoichiometry)
        {
            if (_Modifiers == null) _Modifiers = new Hashtable();
            _Modifiers[node] = stoichiometry;
            node.AddOutgoingEdge(this);
        }
        /// <summary>
        /// Remove a modifier node
        /// </summary>
        /// <param name="node"></param>
        public virtual void RemoveModifier(Node node)
        {
            if (node == null) return;
            if (_Modifiers == null) _Modifiers = new Hashtable();

            if (_Modifiers.Contains(node))
            {
                _Modifiers.Remove(node);
                node.RemoveOutgoingEdge(this);
            }
        }
        /// <summary>
        /// make an edge using the collection of nodes; the first n are the inputs, where n is the second argument
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="numInputs"></param>
        public Edge(ICollection<Node> inputs, ICollection<Node> outputs)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            if (inputs != null && inputs.Count > 0)
                foreach (Node n in inputs)
                {
                    if (n != null)
                    {
                        AddReactant(n);
                    }
                }

            if (outputs != null && outputs.Count > 0)
            foreach (Node n in outputs)
            {
                if (n != null)
                {
                    AddProduct(n);
                }
            }

            if (RateIsZero) Rate = DefaultRate;

            //foreach (Node n in inputs) n.AddOutgoingEdge(this);            
            //foreach (Node n in outputs) n.AddIncomingEdge(this);

            ++COUNT;  _ID = "J" + COUNT;
        }
        /// <summary>
        /// make an edge using the collection of nodes; the first n are the inputs, where n is the second argument
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="numInputs"></param>
        public Edge(ICollection<Node> inputs, ICollection<Node> outputs, string ratelaw)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            if (inputs != null && inputs.Count > 0)
                foreach (Node n in inputs)
                {
                    if (n != null)
                    {
                        AddReactant(n);
                    }
                }

            if (outputs != null && outputs.Count > 0)
                foreach (Node n in outputs)
                {
                    if (n != null)
                    {
                        AddProduct(n);
                    }
                }

            if (IsReaction || _Rate == null) Rate = ratelaw;

            //foreach (Node n in inputs) n.AddOutgoingEdge(this);            
            //foreach (Node n in outputs) n.AddIncomingEdge(this);

            ++COUNT;  _ID = "J" + COUNT;
        }

        /// <summary>
        /// make an edge using the collection of nodes; the first n are the inputs, where n is the second argument
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="numInputs"></param>
        public Edge(ICollection<Node> nodes, int numInputs)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            int i = 0;
            if (nodes != null && nodes.Count > 0)
                foreach (Node n in nodes)
                {
                    if (i < numInputs)
                    {
                        if (n != null)
                        {
                            AddReactant(n);
                        }
                    }
                    else
                    {
                        if (n != null)
                        {
                            AddProduct(n);
                        }
                    }
                    ++i;
                    if (i >= nodes.Count) break;
                }

            if (RateIsZero) Rate = DefaultRate;

            /*i = 0;
            foreach (Node n in nodes)
            {
                if (i < numInputs)
                    n.AddOutgoingEdge(this);
                else
                    n.AddIncomingEdge(this);
                ++i;
                if (i >= nodes.Count) break;
            }  */          

            ++COUNT;  _ID = "J" + COUNT;
        }
        /// <summary>
        /// make an edge using the collection of nodes; the first n are the inputs, where n is the second argument
        /// also specify the rate equation
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="numInputs"></param>
        public Edge(ICollection<Node> nodes, int numInputs, string ratelaw)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            int i = 0;
            if (nodes != null && nodes.Count > 0)
                foreach (Node n in nodes)
                {
                    if (i < numInputs)
                    {
                        if (n != null)
                        {
                            AddReactant(n);
                        }
                    }
                    else
                    {
                        if (n != null)
                        {
                            AddProduct(n);
                        }
                    }
                    ++i;
                    if (i >= nodes.Count) break;
                }

            Rate = ratelaw;
            /*
            i = 0;
            foreach (Node n in nodes)
            {
                if (i < numInputs)
                    n.AddOutgoingEdge(this);
                else
                    n.AddIncomingEdge(this);
                ++i;
                if (i >= nodes.Count) break;
            }*/

            ++COUNT; _ID = "J" + COUNT;
        }

        /// <summary>
        /// Make an edge from the set of nodes with this many inputs. The rate is automatically 
        /// set as the product of the reactants
        /// </summary>
        /// <param name="numInputs"></param>
        /// <param name="nodes"></param>
        public Edge(int numInputs, params Node[] nodes)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            int i = 0;
            if (nodes != null && nodes.Length > 0)
                foreach (Node n in nodes)
                {
                    if (i < numInputs)
                    {
                        if (n != null)
                        {
                            AddReactant(n);
                        }
                    }
                    else
                    {
                        if (n != null)
                        {
                            AddProduct(n);
                        }
                    }
                    ++i;
                    if (i >= nodes.Length) break;
                }

            if (RateIsZero) Rate = DefaultRate;

            /*
            i = 0;
            foreach (Node n in nodes)
            {
                if (i < numInputs)
                    n.AddOutgoingEdge(this);
                else
                    n.AddIncomingEdge(this);
                ++i;
                if (i >= nodes.Length) break;
            }
            */
            ++COUNT; _ID = "J" + COUNT;
        }
        /// <summary>
        /// Make an edge using a set of nodes and this many inputs, and set the rate (forward)
        /// </summary>
        /// <param name="numInputs"></param>
        /// <param name="ratelaw"></param>
        /// <param name="nodes"></param>
        public Edge(int numInputs, string ratelaw, params Node[] nodes)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            int i = 0;
            if (nodes != null && nodes.Length > 0)
                foreach (Node n in nodes)
                {
                    if (i < numInputs)
                    {
                        if (n != null)
                        {
                            AddReactant(n);
                        }
                    }
                    else
                    {
                        if (n != null)
                        {
                            AddProduct(n);
                        }
                    }
                    ++i;
                    if (i >= nodes.Length) break;
                }

            Rate = ratelaw;
            /*
            i = 0;
            foreach (Node n in nodes)
            {
                if (i < numInputs)
                    n.AddOutgoingEdge(this);
                else
                    n.AddIncomingEdge(this);
                ++i;
                if (i >= nodes.Length) break;
            }*/

            ++COUNT; _ID = "J" + COUNT;
        }

        public Edge() : base("edge","")
        { 
            _Rate = "k0";

            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            ++COUNT;  _ID = "rxn" + COUNT;
        }

        /// <summary>
        /// Make an edge with no inputs/output but only a rate equation
        /// </summary>
        /// <param name="rateLaw"></param>
        public Edge(string rateLaw)
            : base("edge", "")
        {
            if (IsReaction || _Rate == null) Rate = rateLaw;

            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            ++COUNT;  _ID = "rxn" + COUNT;
        }

        /// <summary>
        /// Make an edge with a single input and output. Rate is k0*input
        /// </summary>
        /// <param name="inputNode"></param>
        /// <param name="outputNode"></param>
        public Edge(Node inputNode, Node outputNode)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            if (inputNode != null)
            {
                AddReactant(inputNode);
            }
            if (outputNode != null)
            {
                AddProduct(outputNode);
            }
            
            Rate = inputNode.ID;

            //inputNode.AddOutgoingEdge(this);
            //outputNode.AddIncomingEdge(this);

            ++COUNT;  _ID = "J" + COUNT;
        }
        /// <summary>
        /// Make an edge with a single input and output with the given rate law
        /// </summary>
        /// <param name="inputNode"></param>
        /// <param name="outputNode"></param>
        /// <param name="ratelaw"></param>
        public Edge(Node inputNode, Node outputNode, string ratelaw)
            : base("edge", "")
        {
            if (_Reactants == null) _Reactants = new Hashtable();
            if (_Products == null) _Products = new Hashtable();
            if (_Modifiers == null) _Modifiers = new Hashtable();

            if (inputNode != null)
            {
                AddReactant(inputNode);
            }
            if (outputNode != null)
            {
                AddProduct(outputNode);
            }

            Rate = ratelaw;

            //inputNode.AddOutgoingEdge(this);
            //outputNode.AddIncomingEdge(this);

            ++COUNT; _ID = "J" + COUNT;
        }

        /// <summary>
        /// Removes all the input node attachments
        /// </summary>
        public virtual void RemoveAllReactants()
        {
            if (_Reactants == null || _Reactants.Count == 0) return;
            List<Node> list = new List<Node>();
            if (_Reactants != null)
            foreach (Node n in _Reactants.Keys)
                list.Add(n);
            foreach (Node n in list)
            {
                n.RemoveOutgoingEdge(this);
            }
            if (_Reactants == null) _Reactants = new Hashtable();
            _Reactants.Clear();
        }

        /// <summary>
        /// Removes all the output node attachments
        /// </summary>
        public virtual void RemoveAllProducts()
        {
            if (_Products == null || _Products.Count == 0) return;
            List<Node> list = new List<Node>();
            if (_Products != null)
            foreach (Node n in _Products.Keys)
                list.Add(n);
            foreach (Node n in list)
            {
                n.RemoveIncomingEdge(this);
            }
            if (_Products == null) _Products = new Hashtable();
            _Products.Clear();
        }

        /// <summary>
        /// Removes all the output node attachments
        /// </summary>
        public virtual void RemoveAllModifiers()
        {
            if (_Modifiers == null || _Modifiers.Count == 0) return;
            List<Node> list = new List<Node>();
            foreach (Node n in _Modifiers.Keys)
                list.Add(n);
            foreach (Node n in list)
            {
                n.RemoveOutgoingEdge(this);
            }
            if (_Modifiers == null) _Reactants = new Hashtable();
            _Modifiers.Clear();
        }

        /// <summary>
        /// Removes all the nodes attached to this edge
        /// </summary>
        public override void Remove() 
        {
            base.Remove();

            if (_Reactants == null && _Products == null) return;
            
            RemoveAllReactants();
            RemoveAllProducts();
        }

        /// <summary>
        /// replace all pointers to one Node with another -- mainly used by the Module to substitute Nodes with Links
        /// </summary>
        /// <param name="NodeToReplace"></param>
        /// <param name="ReplaceWith"></param>
        public virtual void SubstituteNode(Node NodeToReplace, Node ReplaceWith)
        {
            if (NodeToReplace == null || ReplaceWith == null || NodeToReplace == ReplaceWith) return;
            bool input = false, output = false;// modifier = false;
            if (_Reactants.Contains(NodeToReplace))
            {
                double d = (double)_Reactants[NodeToReplace];
                _Reactants.Remove(NodeToReplace);
                _Reactants[ReplaceWith] = d;
                input = true;
            }
            if (_Products.Contains(NodeToReplace))
            {
                double d = (double)_Products[NodeToReplace];
                _Products.Remove(NodeToReplace);
                _Products[ReplaceWith] = d;
                output = true;
            }
            if (_Modifiers.Contains(NodeToReplace))
            {
                double d = (double)_Modifiers[NodeToReplace];
                _Modifiers.Remove(NodeToReplace);
                _Modifiers[ReplaceWith] = d;
                output = true;
            }
            if (input)
            {
                NodeToReplace.RemoveOutgoingEdge(this);
                ReplaceWith.AddOutgoingEdge(this);
            }
            if (output)
            {
                NodeToReplace.RemoveIncomingEdge(this);
                ReplaceWith.AddIncomingEdge(this);
            }

            if (_Reactants.ContainsKey(ReplaceWith) || _Modifiers.ContainsKey(ReplaceWith))
                ChangeName(NodeToReplace.ID, ReplaceWith.ID);
        }

        /// <summary>
        /// destructor -- calls Remove()
        /// </summary>
        ~Edge()
        {
            Remove();
        }

        public override string ToString()
        {
            return ToString(ParentModule);            
        }

        public virtual string ToString(Module module)
        {
            return ToString(module, true);
        }
        /// <summary>
        /// Generates a string in the format: inputs -> outputs; rate in the context of the given module
        /// use ToString with 3 args if using for several edges in the same module
        /// </summary>
        /// <returns></returns>
        public virtual string ToString(Module module, bool showID)
        {
            StringBuilder inputs = new StringBuilder(),
                          outputs = new StringBuilder(),
                          reaction = new StringBuilder();

            Hashtable allItems = null;
            if (module != null) allItems = module.AllItems;

            //inputs
            if (_Reactants == null || _Reactants.Count < 1)   // no inputs
            {
                inputs.Append(Module.Empty.ID);
            }
            else
            {
                int i = 0;
                foreach (Node n in _Reactants.Keys)  //for each unique inputs
                {
                    Node inputNode = n;    //input and its coeff
                    double coeff = (double)_Reactants[n];

                    if (!string.IsNullOrEmpty(inputNode.ID) &&
                        (
                        //(inputNode is Part) ||
                          ((inputNode is Species) && ((Species)inputNode).GetBoundary(module))
                         ))
                    {
                        //inputs.Append('$'); }
                    }

                    if (!string.IsNullOrEmpty(inputNode.ID) && coeff > 0 && inputNode is Species)
                    {
                        if (coeff > 1)
                        {
                            inputs.Append(coeff);
                        }
                        if (module != null)
                            inputs.Append(module.Rename(inputNode.ID, inputNode.Parent,allItems));
                        else
                            inputs.Append(inputNode.ID);
                    }
                    else
                    {
                        inputs.Append(Module.Empty.ID);    //if no name, use Source
                    }

                    if (i < _Reactants.Count - 1)   //dont put + after the last term
                    {
                        inputs.Append(" + ");
                    }
                    ++i;
                }
            }

            //outputs
            if (_Products == null || _Products.Count < 1)   //no output
            {
                outputs.Append(Module.Empty.ID);
            }
            else
            {
                int i = 0;
                foreach (Node n in _Products.Keys)   //for each unique output
                {
                    Node outputNode = n;   //unique nodes and their coeff
                    double coeff = (double)_Products[n];

                    if (!string.IsNullOrEmpty(outputNode.ID) &&
                    (
                        //(outputNode is Part) ||
                      ((outputNode is Species) && ((Species)outputNode).GetBoundary(module))
                     ))
                    {
                        // outputs.Append('$');
                    }
                    if (!string.IsNullOrEmpty(outputNode.ID) && coeff > 0)// && outputNode is Species)
                    {
                        if (coeff > 1)
                        {
                            outputs.Append(coeff);
                        }
                        if (module != null)
                            outputs.Append(module.Rename(outputNode.ID, outputNode.Parent,allItems));
                        else
                            outputs.Append(outputNode.ID);
                    }
                    else
                    {
                        outputs.Append(Module.Empty.ID);    //if no name, use Source
                    }
                    if (i < _Products.Count - 1)   //dont put + after the last term
                    {
                        outputs.Append(" + ");
                    }
                    ++i;
                }

                //the reaction
                if (!string.IsNullOrEmpty(ID) && showID)   // reaction name : 
                {
                    if (module != null)
                        reaction.Append(module.Rename(ID, Parent, allItems));
                    else
                        reaction.Append(ID);
                    reaction.Append(": ");
                }
            }

            //the reaction
            reaction.Append(inputs);
            reaction.Append(" -> ");
            reaction.Append(outputs);
            reaction.Append("; ");
            reaction.Append(GetRate(module));
            reaction.Append("; ");

            return reaction.ToString();
        }

        /// <summary>
        /// Generates a string in the format: inputs -> outputs; rate in the context of the given module
        /// avoid recalculating Module.AllItems by providing it as an argument
        /// </summary>
        /// <returns></returns>
        public virtual string ToString(Module module, Hashtable allItems, bool showID)
        {
            StringBuilder inputs = new StringBuilder(),
                          outputs = new StringBuilder(),
                          reaction = new StringBuilder();

            //inputs
            if (_Reactants == null || _Reactants.Count < 1)   // no inputs
            {
                inputs.Append(Module.Empty.ID);
            }
            else
            {
                int i = 0;
                foreach (Node n in _Reactants.Keys)  //for each unique inputs
                {
                    Node inputNode = n;    //input and its coeff
                    double coeff = (double)_Reactants[n];

                    if (!string.IsNullOrEmpty(inputNode.ID) &&
                        (
                        //(inputNode is Part) ||
                          ((inputNode is Species) && ((Species)inputNode).GetBoundary(module))
                         ))
                        inputs.Append('$');

                    if (!string.IsNullOrEmpty(inputNode.ID) && coeff > 0 && inputNode is Species)
                    {
                        if (coeff > 1)
                        {
                            inputs.Append(coeff);
                        }
                        if (module != null)
                            inputs.Append(module.Rename(inputNode.ID, inputNode.Parent, allItems));
                        else
                            inputs.Append(inputNode.ID);
                    }
                    else
                    {
                        inputs.Append(Module.Empty.ID);    //if no name, use Source
                    }

                    if (i < _Reactants.Count - 1)   //dont put + after the last term
                    {
                        inputs.Append(" + ");
                    }
                    ++i;
                }
            }

            //outputs
            if (_Products == null || _Products.Count < 1)   //no output
            {
                outputs.Append(Module.Empty.ID);
            }
            else
            {
                int i = 0;
                foreach (Node n in _Products.Keys)   //for each unique output
                {
                    Node outputNode = n;   //unique nodes and their coeff
                    double coeff = (double)_Products[n];

                    if (!string.IsNullOrEmpty(outputNode.ID) &&
                    (
                        //(outputNode is Part) ||
                      ((outputNode is Species) && ((Species)outputNode).GetBoundary(module))
                     ))
                        outputs.Append('$');

                    if (!string.IsNullOrEmpty(outputNode.ID) && coeff > 0)// && outputNode is Species)
                    {
                        if (coeff > 1)
                        {
                            outputs.Append(coeff);
                        }
                        if (module != null)
                            outputs.Append(module.Rename(outputNode.ID, outputNode.Parent, allItems));
                        else
                            outputs.Append(outputNode.ID);
                    }
                    else
                    {
                        outputs.Append(Module.Empty.ID);    //if no name, use Source
                    }
                    if (i < _Products.Count - 1)   //dont put + after the last term
                    {
                        outputs.Append(" + ");
                    }
                    ++i;
                }

                //the reaction
                if (!string.IsNullOrEmpty(ID) && showID)   // reaction name : 
                {
                    if (module != null)
                        reaction.Append(module.Rename(ID, Parent, allItems));
                    else
                        reaction.Append(ID);
                    reaction.Append(": ");
                }
            }

            //the reaction
            reaction.Append(inputs);
            reaction.Append(" -> ");
            reaction.Append(outputs);
            reaction.Append("; ");
            reaction.Append(GetRate(module));
            reaction.Append("; ");

            return reaction.ToString();
        }

        /// <summary>
        /// The edge must have a species as an output and a valid rate law
        /// </summary>
        /// <returns></returns>
        public virtual bool IsReaction
        {
            get
            {
                if (_Reactants == null || _Products == null || (string.IsNullOrEmpty(_Rate)))
                    return false;

                if (IsRegulation) return false;

                foreach (Node n in _Reactants.Keys)
                    if (n is Species)
                        return true;

                foreach (Node n in _Products.Keys)
                    if (n is Species)
                        return true;

                return false;
            }
        }

        /// <summary>
        /// Assign parameters new names so that they would not interfere with other parameters
        /// </summary>
        public virtual void AssignParameterNames()
        {
            Module parent = ParentModule;
            if (parent != null)
            {
                parent.AssignParametersToReaction(this);
            }
        }

        /// <summary>
        /// Writes the XML for this class
        /// </summary>
        /// <param name="writer">xml writer</param>
        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);
            writer.WriteStartElement("Edge", "");

            Module root = Root as Module;

            Hashtable allitems = null;
            if (root != null) allitems = root.AllItemsWithPrefix;

            foreach (Node n in _Reactants.Keys)
            {
                writer.WriteStartElement("Reactant", "");

                writer.WriteStartElement("ID", "");
                
                if (root != null)
                    writer.WriteString(root.Rename(n.ID,n.Parent,allitems,false));
                else
                    writer.WriteString(n.ID);

                writer.WriteEndElement();

                writer.WriteStartElement("Stoichiometry", "");
                writer.WriteValue((double)_Reactants[n]);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }

            foreach (Node n in _Products.Keys)
            {
                writer.WriteStartElement("Product", "");

                writer.WriteStartElement("ID", "");
                if (root != null)
                    writer.WriteString(root.Rename(n.ID, n.Parent, allitems,false));
                else
                    writer.WriteString(n.ID);
                writer.WriteEndElement();

                writer.WriteStartElement("Stoichiometry", "");
                writer.WriteValue((double)_Products[n]);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }            

            foreach (Node n in _Modifiers.Keys)
            {
                writer.WriteStartElement("Modifier", "");

                writer.WriteStartElement("ID", "");
                if (root != null)
                    writer.WriteString(root.Rename(n.ID, n.Parent, allitems,false));
                else
                    writer.WriteString(n.ID);
                writer.WriteEndElement();

                writer.WriteStartElement("Stoichiometry", "");
                writer.WriteValue((double)_Modifiers[n]);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }

            writer.WriteStartElement("Rate", "");
            writer.WriteString(Rate);
            writer.WriteEndElement();

            writer.WriteStartElement("IsRegulation", "");
            writer.WriteValue(IsRegulation);
            writer.WriteEndElement();

            writer.WriteEndElement();

        }

        /// <summary>
        /// Reads the information from an XML file
        /// </summary>
        /// <param name="reader">xlm reader</param>
        public override bool ReadXML(XmlReader reader)
        {
            AUTO_MODIFIERS = false;

            bool read = base.ReadXML(reader);

            if (!(reader.NodeType == XmlNodeType.Element && reader.Name == "Edge"))
                 read = reader.Read();
            
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "Edge")
            {
                reader.Read();
                Module module = ParentModule;

                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Reactant":
                                read = reader.Read();
                                ReadXMLNode("reactant", reader);
                                break;
                            case "Product":
                                read = reader.Read();
                                ReadXMLNode("product", reader);
                                break;
                            case "Modifier":
                                read = reader.Read();
                                ReadXMLNode("modifier", reader);
                                break;
                            case "Rate":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    Rate = reader.ReadContentAsString();
                                break;
                            case "IsRegulation":
                                reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    IsRegulation = bool.Parse(reader.ReadContentAsString());
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Edge")) break;
                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }

                if (_Reactants.Count == 0) AddReactant(Module.Empty, 1d);
                if (_Products.Count == 0) AddProduct(Module.Empty, 1d);

                AUTO_MODIFIERS = true;

                return true;
            }

            AUTO_MODIFIERS = true;
            return false;
        }

        void ReadXMLNode(string type, XmlReader reader)
        {
            bool read = true;
            string id = null;
            double stoic = 1d;
            while (read)
            {
                if (reader.NodeType == XmlNodeType.EndElement)
                    break;

                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "ID":
                            read = reader.Read();
                            if (reader.NodeType == XmlNodeType.Text)
                                id = reader.ReadContentAsString();
                            break;
                        case "Stoichiometry":
                            read = reader.Read();
                            if (reader.NodeType == XmlNodeType.Text)
                            {
                                try { stoic = double.Parse(reader.ReadContentAsString()); }
                                catch (Exception) { stoic = 1d; }
                            }
                            break;
                        default:
                            while (reader.NodeType != XmlNodeType.EndElement)
                                read = reader.Read();
                            break;
                    }
                }

                if (!read) break;
                read = reader.Read();
            }

            Module module = Root as Module;
            if (!string.IsNullOrEmpty(id) && module != null)
            {
                Node n = module.FindItem(id,module.AllItemsWithPrefix) as Node;
                if (n != null)
                {
                    switch (type)
                    {
                        case "modifier":
                            AddModifier(n, stoic);
                            break;
                        case "reactant":
                            AddReactant(n, stoic);
                            break;
                        default:
                            AddProduct(n, stoic);
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// finds how the rate is changing with respect to the given variable
        /// </summary>
        /// <param name="rate">rate formula</param>
        /// <param name="token">varibale (a string) in the rate formula</param>
        /// <returns>the change in rate with variable is increased by 1</returns>
        public static double RateChangeWithRespectTo(string rate, string token)
        {
            Functions.TAlgExpression ae = new Functions.TAlgExpression();
            ae.AssignBehaviour = Functions.TAssignBehaviour.eInstallSymbols;
            ae.compile(rate);
            Functions.TTreeNode[] nodes = ae.GetAllSymbols();
            foreach (Functions.TTreeNode tn in nodes)
            {
                tn.value = 2.0f;
            }
            double x1 = ae.eval();

            foreach (Functions.TTreeNode tn in nodes)
            {
                if (tn.name == token)
                    tn.value = 3.0f;
            }

            double x2 = ae.eval();

            return x2 - x1;
        }
    }
}
